﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Configuration;
using System.Web.Mvc;
using System.Net;
using System.IO;
using System.Xml.Serialization;
using System.Globalization;
using SelfMadeMillionaire.Core.Services;
using SelfMadeMillionaire.Core.DTOs;
using SelfMadeMillionaire.Core.Entities;
using SelfMadeMillionaire.Core;

namespace SelfMadeMillionaire.Web.Infrastructure.Controllers
{
    public class PayPalController : BaseController
    {
        readonly IPaymentService _paymentService;
        readonly IEmailService _emailService;

        public PayPalController(IPaymentService paymentService, IEmailService emailService)
        {
            _paymentService = paymentService;
            _emailService = emailService;
        }

        public void IPN()
        {
            string logMessage = "";
            PayPalPaymentDto dto = null;

            try
            {
#if DEBUG
                string message = "";
                dto = CreateRecordInDB(out message);
#else

                if (IsVerifiedNotification())
                {
                    //check the payment_status is Completed or Refunded or Reversed
                    bool status = (Request.Form["payment_status"] == "Completed") || (Request.Form["payment_status"] == "Refunded") || (Request.Form["payment_status"] == "Reversed");

                    if (!status)
                        logMessage += String.Format("PayPal Response has payment_status: {0}", Request.Form["payment_status"]);

                    if (status)
                    {
                        logMessage += "\r\n PayPal Response has verified successfully and transaction goes to DB";
                        string message = "";
                        dto = CreateRecordInDB(out message);

                        logMessage += "\r\n" + message;
                    }
                    else
                        logMessage += "\r\n PayPal Response has verified successfully but transaction doesn't go to DB";
                }
                else
                {
                    logMessage = "PayPal Response is NOT verified successfully";
                }

                new LoggingUtility().WriteErrorFormat(logMessage + "\r\n" + Obj2XmlStr(InitPayPalInfoFromRequest()));
#endif

                if (dto != null)
                {
                    _emailService.SendPayPalConfirmEmail(dto.Payment.Order.Subscriber, dto.Payment.ItemName, dto.Payment.Order.ExperationDate);
                }
			}
            catch (Exception ex)
            {
                StringBuilder message = new StringBuilder("PayPalIPNHandler exception detected");

                for (Exception currentException = ex; currentException != null;
                    currentException = currentException.InnerException)
                {
                    message.AppendFormat("\r\ntype={0}\r\nmessage={1}\r\nstack=\r\n{2}",
                        currentException.GetType().FullName,
                        currentException.Message,
                        currentException.StackTrace);
                }
				new LoggingUtility().WriteErrorFormat(message.ToString());

            }
        }
        private bool IsVerifiedNotification()
        {
            string response = "";

            //string post = Request.Form.ToString() + "&cmd=_notify-validate";
            string post = Encoding.ASCII.GetString(Request.BinaryRead(Request.ContentLength)) + "&cmd=_notify-validate";
            string serverUrl = string.Equals(WebConfigurationManager.AppSettings["isPayPalSandbox"], "true", StringComparison.InvariantCultureIgnoreCase) ?
              "https://www.sandbox.paypal.com/us/cgi-bin/webscr" :
              "https://www.paypal.com/us/cgi-bin/webscr";

            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(serverUrl);
            req.Method = "POST";
            req.ContentType = "application/x-www-form-urlencoded";
            req.ContentLength = post.Length;

            using (StreamWriter writer = new StreamWriter(req.GetRequestStream(), System.Text.Encoding.ASCII))
                writer.Write(post);

            using (var resp = (HttpWebResponse)req.GetResponse())
            using (var sr = new StreamReader(resp.GetResponseStream()))
                response = sr.ReadToEnd();

            return (response == "VERIFIED");
        }


        private PayPalPaymentDto CreateRecordInDB(out string message)
        {
#if DEBUG
            var dto = InitPayPalInfoWithTestData();
#else
            var dto = InitPayPalInfoFromRequest();
#endif

            _paymentService.PayPalProcess(dto);

            message = "CreateRecotdInDB - success";
            return dto;
        }

        private PayPalPaymentDto InitPayPalInfoFromRequest()
        {
            // 4/5
            var transaction = new PayPalTransaction();
            transaction.testIpn = Convert.ToInt32(Request.Form["test_ipn"]);
            transaction.txnId = Request.Form["txn_id"];
            transaction.txnType = Request.Form["txn_type"];
            transaction.verifySign = Request.Form["verify_sign"];
            transaction.parentTxnId = Request.Form["parent_txn_id"];

            // 12/16
            var payment = new PayPalPayment();
            payment.Custom = Request.Form["custom"];
            payment.ItemName = Request.Form["item_name"];
            payment.ItemNumber = Request.Form["item_number"];
            payment.McCurrency = Request.Form["mc_currency"];
            payment.McFee = Convert.ToDouble(Request.Form["mc_fee"], CultureInfo.CreateSpecificCulture("en-US"));
            payment.McGross = Convert.ToDouble(Request.Form["mc_gross"], CultureInfo.CreateSpecificCulture("en-US"));

            string paymentDate = Request.Form["payment_date"];
            // remove sql incompatible datetime chars
            if (paymentDate != null)
            {
                paymentDate = paymentDate.Replace(".", "");
                paymentDate = paymentDate.Replace("PDT", "");
                paymentDate = paymentDate.Replace("PST", "");

                payment.PaymentDate = DateTime.Parse(paymentDate, new CultureInfo("en-US", true)).ToString();
            }
            else
                payment.PaymentDate = DateTime.Now.ToString();

            payment.PaymentType = Request.Form["payment_type"];
            payment.PaymentStatus = Request.Form["payment_status"];
            payment.Quantity = Convert.ToInt32(Request.Form["quantity"]);
            payment.Shipping = Convert.ToDouble(Request.Form["shipping"], CultureInfo.CreateSpecificCulture("en-US"));
            payment.Tax = Convert.ToDouble(Request.Form["tax"], CultureInfo.CreateSpecificCulture("en-US"));

            // 12/14
            var buyer = new PayPalBuyer();
            buyer.payerEmail = Request.Form["payer_email"];
            buyer.payerId = Request.Form["payer_id"];
            buyer.payerStatus = Request.Form["payer_status"];
            buyer.firstName = Request.Form["first_name"];
            buyer.lastName = Request.Form["last_name"];
            buyer.addressCity = Request.Form["address_city"];
            buyer.addressCountry = Request.Form["address_country"];
            buyer.addressCountryCode = Request.Form["address_country_code"];
            buyer.addressName = Request.Form["address_name"];
            buyer.addressState = Request.Form["address_state"];
            buyer.addressStatus = Request.Form["address_status"];
            buyer.addressStreet = Request.Form["address_street"];
            buyer.addressZip = Request.Form["address_zip"];

            var ppInfo = new PayPalPaymentDto()
            {
                Transaction = transaction,
                Payment = payment,
                Buyer = buyer
            };

            return ppInfo;
        }


        //it can be used for testing
        private PayPalPaymentDto InitPayPalInfoWithTestData()
        {
            //for testing db operation
            var transaction = new PayPalTransaction();
            transaction.testIpn = 1;
            transaction.txnId = "5LD7668601993174V";
            transaction.txnType = "web_accept";
            transaction.verifySign = "AFcWxV21C7fd0v3bYYYRCpSSRl31A.DBTUguJkezmOirAChPLgmwzu1r";

            // 12/16
            var payment = new PayPalPayment();
			payment.Custom = "test user";
            payment.ItemName = "";
            payment.ItemNumber = "";
            payment.McCurrency = "USD";
            payment.McFee = Convert.ToDouble("0.59", CultureInfo.CreateSpecificCulture("en-US"));
            payment.McGross = Convert.ToDouble("10.00", CultureInfo.CreateSpecificCulture("en-US"));

            string paymentDate = "02:43:52 Dec. 16, 2009 PST";
            // remove sql incompatible datetime chars
            paymentDate = paymentDate.Replace(".", "");
            paymentDate = paymentDate.Replace("PDT", "");
            paymentDate = paymentDate.Replace("PST", "");
            payment.PaymentDate = DateTime.Parse(paymentDate, new CultureInfo("en-US", true)).ToString();//Convert.ToDateTime(paymentDate, new CultureInfo("en-US", true));

            payment.PaymentType = "instant";
            payment.PaymentStatus = "Completed";
            payment.Quantity = 1;
            payment.Shipping = Convert.ToDouble("0.00", CultureInfo.CreateSpecificCulture("en-US"));
            payment.Tax = Convert.ToDouble("0.00", CultureInfo.CreateSpecificCulture("en-US"));

            // 12/14
            var buyer = new PayPalBuyer();
            buyer.payerEmail = "ppsand_1254788532_per@pokerstan.com";
            buyer.payerId = "Y428GTA9VMXQG";
            buyer.payerStatus = "verified";
            buyer.firstName = "Test";
            buyer.lastName = "User";
            buyer.addressCity = "San Jose";
            buyer.addressCountry = "United States";
            buyer.addressCountryCode = "US";
            buyer.addressName = "John Smith";
            buyer.addressState = "CA";
            buyer.addressStatus = "confirmed";
            buyer.addressStreet = "123, any street";
            buyer.addressZip = "95131";

            var ppInfo = new PayPalPaymentDto()
            {
                Transaction = transaction,
                Payment = payment,
                Buyer = buyer
            };

            return ppInfo;
        }

        public  static string Obj2XmlStr(PayPalPaymentDto payPalInfo)
        {
            XmlSerializer sr = new XmlSerializer(payPalInfo.GetType());

            StringBuilder sb = new StringBuilder();
            StringWriter w = new StringWriter(sb, System.Globalization.CultureInfo.InvariantCulture);

            sr.Serialize(w, payPalInfo);

            return sb.ToString();
        }
    }
}